`include "common_def.v"
`define MTIMECMP 64'h0000_0000_0200_4000
`define MTIME 64'h0000_0000_0200_BFF8
`define MSIP 64'h0000_0000_0200_0000
//0x0200_0000~0x0200_ffff
module MODULE_CLINT (
input                    clk_i,
input                    rst_i,
input   [`WIDTH-1:0] 		 clint_addr_i,
input                    clint_ren_i,
output  [`WIDTH-1:0] 		 clint_rdata_o,
input                    clint_wen_i,
input   [`WIDTH-1:0] 		 clint_wdata_i,
input		[7:0]						 clint_wmask_i,
output									 clint_rend_o,
output									 clint_wend_o,
output                   mtip_o,
output									 msip_o,
input										 is_clint_i
);

Reg #(1,0) wend_delay_o(clk_i,rst_i,clint_wend,clint_wend_o,1);
Reg #(1,0) rend_delay_o(clk_i,rst_i,clint_rend,clint_rend_o,1);
Reg #(`WIDTH,0) clint_rdata_o_reg(clk_i,rst_i,clint_rdata[`WIDTH-1:0],clint_rdata_o[`WIDTH-1:0],1);
wire clint_wend;
wire clint_rend;
assign clint_wend = is_clint_i?clint_wen_i:0;
assign clint_rend = is_clint_i?clint_ren_i:0;
wire [`WIDTH-1:0] wmask;
assign wmask[`WIDTH-1:0] = {{8{clint_wmask_i[7]}},{8{clint_wmask_i[6]}},{8{clint_wmask_i[5]}},{8{clint_wmask_i[4]}},{8{clint_wmask_i[3]}},{8{clint_wmask_i[2]}},{8{clint_wmask_i[1]}},{8{clint_wmask_i[0]}}};
wire [`WIDTH-1:0] mtime, mtimecmp; 
wire msip;

// read:
wire ren_mtimecmp, ren_mtime,ren_msip;
wire [`WIDTH-1:0] clint_rdata;
assign ren_mtimecmp = clint_ren_i & ({clint_addr_i[`WIDTH-1:3],3'b0} == `MTIMECMP);
assign ren_mtime    = clint_ren_i & ({clint_addr_i[`WIDTH-1:3],3'b0} == `MTIME);
assign ren_msip    = clint_ren_i & (clint_addr_i[`WIDTH-1:0] == `MSIP);
MuxKeyWithDefault #(3,3,`WIDTH) clint_data_mux(clint_rdata[`WIDTH-1:0],{ren_mtimecmp,ren_mtime,ren_msip},0,{
	3'b001,{{`WIDTH-1{1'b0}},msip},
	3'b010,mtime[`WIDTH-1:0],
	3'b100,mtimecmp[`WIDTH-1:0]
});
assign clint_rdata = ren_mtime ? mtime : (ren_mtimecmp ? mtimecmp : 64'b0);
// write:
wire  wen_mtimecmp, wen_mtime , wen_msip;
wire [`WIDTH-1:0] wdata_mtimecmp, wdata_mtime;
wire wmsip;

assign wen_mtimecmp   = clint_wen_i & ({clint_addr_i[`WIDTH-1:3],3'b0} == `MTIMECMP);
assign wen_mtime 		  = clint_wen_i & ({clint_addr_i[`WIDTH-1:3],3'b0} == `MTIME   );
assign wen_msip				= clint_wen_i & (clint_addr_i[`WIDTH-1:0] == `MSIP);
assign wdata_mtimecmp = (clint_wdata_i & wmask) |(mtimecmp &(~wmask)) ;
assign wdata_mtime    = wen_mtime ? (clint_wdata_i&wmask)|(mtime &(~wmask)) : (mtime + 1);
assign wmsip 					= clint_wdata_i[0];

Reg #(`WIDTH,0) mtime_reg(clk_i,rst_i,wdata_mtime[`WIDTH-1:0],mtime[`WIDTH-1:0],1);
Reg #(`WIDTH,0) mtimecmp_reg(clk_i,rst_i,wdata_mtimecmp[`WIDTH-1:0],mtimecmp[`WIDTH-1:0],wen_mtimecmp);
Reg #(1,0) msip_reg(clk_i,rst_i,wmsip,msip,wen_msip);

assign mtip_o = (mtime >= mtimecmp);
assign msip_o = msip;

endmodule
